package com.allinpay.controller;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.UsernamePasswordToken;
import org.apache.shiro.config.IniSecurityManagerFactory;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.util.Factory;

/**
 * @author : wushikai
 * <p>
 * date : 2020-09-01
 */
public class LoginController {


    private final static Log log = LogFactory.getLog(LoginController.class);

    /**
     * IniSecurityManagerFactory--ini文件---> Factory<SecurityManager>
     * <p>
     * Factory<SecurityManager> getInstance()---> securityManager
     * <p>
     * 获取 SecurityManager 并绑定到 SecurityUtils，这是一个全局设置，设置一次即可
     * <p>
     * SecurityUtils 根据 SecurityManager 获取到 subject
     * <p>
     * 设置你的 usernamePasswordToken --> new UsernamePasswordToken("admin", "123456");
     * <p>
     * 使用 subject.loginByIniFile(usernamePasswordToken);  这个方法和小白入门的想法是不同的, 这个是要抛异常的, 而不是说只是给你返回false
     * <p>
     * 最后使用 subject.logout(); 即可
     * <p>
     * 如果身份验证失败请捕获 AuthenticationException 或其子类，
     * 常见的如： DisabledAccountException（禁用的帐号）、
     * LockedAccountException（锁定的帐号）、
     * UnknownAccountException（错误的帐号）、
     * ExcessiveAttemptsException（登录失败次数过多）、
     * IncorrectCredentialsException （错误的凭证）、
     * ExpiredCredentialsException（过期的凭证）等，具体请查看其继承关系；
     * <p>
     * 比如, 故意报错:
     * [ERROR] LoginController - loginByIniFile fail
     * org.apache.shiro.authc.IncorrectCredentialsException:
     * at com.allinpay.LoginController.loginByIniFile(LoginController.java:61)
     * at com.allinpay.LoginController.main(LoginController.java:85)
     * <p>
     * <p>
     * 这是最简单的demo了, 以后复杂的企业级 shiro 用法还有:
     * <p>
     * <p>
     * 用户名 / 密码硬编码在 ini 配置文件，以后需要改成如数据库存储，且密码需要加密存储；
     * 用户身份 Token 可能不仅仅是用户名 / 密码，也可能还有其他的，如登录时允许用户名 / 邮箱 / 手机号同时登录。
     */
    public static void loginByIniFile(String fileName) {

        // fileName = "classpath:shiro.ini";

        //1、获取SecurityManager工厂，此处使用Ini配置文件初始化SecurityManager
        Factory<SecurityManager> factory = new IniSecurityManagerFactory(fileName);


        //2、得到SecurityManager实例 并绑定给SecurityUtils
        SecurityManager securityManager = factory.getInstance();

        SecurityUtils.setSecurityManager(securityManager);


        //3、得到Subject及创建用户名/密码身份验证Token（即用户身份/凭证）
        Subject subject = SecurityUtils.getSubject();
        UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken("admin", "123123");


        try {
            //4、登录，即身份验证
            subject.login(usernamePasswordToken);
            log.info("loginByIniFile success");
        } catch (AuthenticationException e) {
            log.error("loginByIniFile fail");
            e.printStackTrace();
            //5、身份验证失败
        }
        //  Assert.assertEquals(true, subject.isAuthenticated()); //断言用户已经登录
        boolean authenticated = subject.isAuthenticated();
        if (authenticated) {
            log.info("用户已经认证!!");

        }
        //6、退出
        subject.logout();

    }


    //错误的方式,
    public static void loginByMyRealm1() {

        IniSecurityManagerFactory factory = new IniSecurityManagerFactory("classpath:shiro-my-realm1.ini");
        //2、得到SecurityManager实例 并绑定给SecurityUtils
        org.apache.shiro.mgt.SecurityManager securityManager = factory.getInstance();


        SecurityUtils.setSecurityManager(securityManager);
        Subject subject = SecurityUtils.getSubject();
        UsernamePasswordToken usernamePasswordToken = new UsernamePasswordToken("admin", "123");
        try {
            securityManager.login(subject, usernamePasswordToken);

            log.info("login success");
        } catch (AuthenticationException e) {
            log.error("login fail");
            e.printStackTrace();
            //5、身份验证失败
        }


    }


    public static void loginByJDBCRealm() {
        //1、获取SecurityManager工厂，此处使用Ini配置文件初始化SecurityManager
        Factory<SecurityManager> factory =
                new IniSecurityManagerFactory("classpath:shiro-jdbc-realm.ini");

        //2、得到SecurityManager实例 并绑定给SecurityUtils
        org.apache.shiro.mgt.SecurityManager securityManager = factory.getInstance();
        SecurityUtils.setSecurityManager(securityManager);

        //3、得到Subject及创建用户名/密码身份验证Token（即用户身份/凭证）
        Subject subject = SecurityUtils.getSubject();
        UsernamePasswordToken token = new UsernamePasswordToken("admin", "123");

        try {
            //4、登录，即身份验证
            subject.login(token);
            log.info("login success");
        } catch (AuthenticationException e) {
            log.error("失败");
            //5、身份验证失败
            e.printStackTrace();
        }

        //Assert.assertEquals(true, subject.isAuthenticated()); //断言用户已经登录

        //6、退出
        subject.logout();
    }

    public static void main(String[] args) {
        // loginByIniFile("classpath:shiro-my-realm1.ini");
        // loginByMyRealm1( );
        loginByJDBCRealm();


    }


}

/*

    流程如下：

    首先调用 Subject.loginByIniFile(token) 进行登录，其会自动委托给 Security Manager，调用之前必须通过 SecurityUtils.setSecurityManager() 设置；
    SecurityManager 负责真正的身份验证逻辑；它会委托给 Authenticator 进行身份验证；
    Authenticator 才是真正的身份验证者，Shiro API 中核心的身份认证入口点，此处可以自定义插入自己的实现；
    Authenticator 可能会委托给相应的 AuthenticationStrategy 进行多 Realm 身份验证，默认 ModularRealmAuthenticator 会调用
    AuthenticationStrategy 进行多 Realm 身份验证；
    Authenticator 会把相应的 token 传入 Realm，从 Realm 获取身份验证信息，如果没有返回 / 抛出异常表示身份验证失败了。
    此处可以配置多个 Realm，将按照相应的顺序及策略进行访问。


*/


